namespace ScrumSprintMonitor.ConfigEditor.Views.PresentationConfigurationEditorView
{
	#region #using Directives

	using System;
	using System.Collections.Generic;
	using System.Collections.ObjectModel;
	using System.Globalization;
	using System.Reflection;
	using System.Windows;
	using System.Windows.Input;
	using System.Windows.Media;

	using Infrastructure;
	using Infrastructure.Configuration;

	using Microsoft.Practices.Composite.Presentation.Commands;

	#endregion

	public class PresentationConfigurationEditorViewModel : IPresentationConfigurationEditorViewModel
	{
		private readonly ICommand cancelCommand;
		private readonly IPresentationConfiguration configuration;
		private readonly ICommand okCommand;

		private readonly ObservableCollection<RemainingWorkStatusRatioUpperLimitEntry> remainingWorkStatusRatioUpperLimits =
			new ObservableCollection<RemainingWorkStatusRatioUpperLimitEntry>();

		private readonly Color[] standardColors;

		public PresentationConfigurationEditorViewModel(IPresentationConfiguration configuration)
		{
			this.configuration = configuration;

			this.okCommand = new DelegateCommand<Window>(OnOK, CanCloseWithOK);
			this.cancelCommand = new DelegateCommand<Window>(OnCancel, CanCloseWithCancel);

			for (var status = RemainingWorkStatus.NoWorkLeft; status <= RemainingWorkStatus.SeverelyOverassigned; ++status)
			{
				var entry = new RemainingWorkStatusRatioUpperLimitEntry(this.configuration.GetRemainingWorkStatusPresentationConfig(status));
				this.remainingWorkStatusRatioUpperLimits.Add(entry);
			}

			// Prepare colors list
			var colorProperties = typeof(Colors).GetProperties();
			var colorsList = new List<Color>();
			foreach (var propertyInfo in colorProperties)
			{
				colorsList.Add((Color) propertyInfo.GetValue(null, BindingFlags.Static, null, null, CultureInfo.CurrentCulture));
			}
			colorsList.Sort(ColorSorter);

			this.standardColors = colorsList.ToArray();
		}

		#region Implementation of IPresentationConfigurationEditorViewModel

		public Color[] Colors
		{
			get { return this.standardColors; }
		}

		public ObservableCollection<RemainingWorkStatusRatioUpperLimitEntry> RemainingWorkStatusRatioUpperLimits
		{
			get { return this.remainingWorkStatusRatioUpperLimits; }
		}

		public bool IsReadOnly
		{
			get { return this.configuration.IsReadOnly; }
		}

		public void Save()
		{
			this.configuration.Save();
		}

		public ICommand OkCommand
		{
			get { return this.okCommand; }
		}

		public ICommand CancelCommand
		{
			get { return this.cancelCommand; }
		}

		#endregion

		#region Commands

		private static bool CanCloseWithCancel(Window configurationWindow)
		{
			return true;
		}

		private static void OnCancel(Window configurationWindow)
		{
			configurationWindow.DialogResult = false;
			configurationWindow.Close();
		}

		private static bool CanCloseWithOK(Window configurationWindow)
		{
			return true;
		}

		private static void OnOK(Window configurationWindow)
		{
			configurationWindow.DialogResult = true;
			configurationWindow.Close();
		}

		#endregion

		private static int ColorSorter(Color c1, Color c2)
		{
			double h1, s1, v1;
			double h2, s2, v2;
			RgbToHsv(c1.R, c1.G, c1.B, out h1, out s1, out v1);
			RgbToHsv(c2.R, c2.G, c2.B, out h2, out s2, out v2);

			int result = -s1.CompareTo(s2);
			if (result == 0)
			{
				result = v1.CompareTo(v2);

				if (result == 0)
				{
					result = h1.CompareTo(h2);
				}
			}

			return result;
		}

		private static void RgbToHsv(byte cr, byte cg, byte cb, out double ph, out double ps, out double pv)
		{
			double r = cr / 255.0;
			double g = cg / 255.0;
			double b = cb / 255.0;
			double max = Math.Max(r, (Math.Max(g, b)));
			double min = Math.Min(r, (Math.Min(g, b)));
			ph = 0.0;
			pv = max; /* Calculate saturation */
			if (max != 0.0)
			{
				ps = (max - min) / max;
			}
			else
			{
				ps = 0.0;
			}
			if (ps == 0.0)
			{
				ph = 0.0;
				//UNDEFINED;                     ;
				return;
			}
			/* chromatic case: Saturation is not 0, so determine hue */
			double delta = max - min;
			if (r == max)
			{
				ph = (g - b) / delta;
			}
			else if (g == max)
			{
				ph = 2.0 + (b - r) / delta;
			}
			else if (b == max)
			{
				ph = 4.0 + (r - g) / delta;
			}
			ph = ph * 60.0;
			if (ph < 0.0)
			{
				ph += 360.0;
			}
		}
	}
}